From: Chris Swan Date: Mon, 23 Jun 2025 12:34:50 +0000 (+0100) Subject: luci-app-csshnpd: Add new package X-Git-Url: http://git.openwrt.org/%22https:/collectd.org//%22http:/www.crowdsec.net/%22/%22https:/collectd.org/%22http:/www.crowdsec.net/%22?a=commitdiff_plain;h=834b5d06e3cace777f853321db9a0f71423e2e68;p=project%2Fluci.git luci-app-csshnpd: Add new package Adding LuCI web interface for csshnpd package Signed-off-by: Chris Swan --- diff --git a/applications/luci-app-csshnpd/Makefile b/applications/luci-app-csshnpd/Makefile new file mode 100644 index 0000000000..9c06a4e6fc --- /dev/null +++ b/applications/luci-app-csshnpd/Makefile @@ -0,0 +1,15 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=luci-app-csshnpd +PKG_LICENSE:=GPL-2.0 +PKG_MAINTAINER:=Chris Swan +PKG_RELEASE:=1 + +LUCI_TITLE:=NoPorts Web UI +LUCI_DESCRIPTION:=LuCI config app for NoPorts daemon (csshnpd) +LUCI_DEPENDS:=+luci-base +csshnpd +LUCI_PKGARCH:=all + +include ../../luci.mk + +# call BuildPackage - OpenWrt buildroot signature diff --git a/applications/luci-app-csshnpd/htdocs/luci-static/resources/view/sshnpd/config.js b/applications/luci-app-csshnpd/htdocs/luci-static/resources/view/sshnpd/config.js new file mode 100644 index 0000000000..1e9855cec4 --- /dev/null +++ b/applications/luci-app-csshnpd/htdocs/luci-static/resources/view/sshnpd/config.js @@ -0,0 +1,88 @@ +'use strict'; +'require view'; +'require form'; + +function validateAtsign(section_id, value) { + if (value.length < 1) { + return _('Must not be empty and should start with @ (e.g., "@a").'); + } + return true; +} + +function validateDevice(section_id, value) { + if (value.length < 1) { + return _('Must be at least one character long (e.g., "a").'); + } + if (value.length == 1) { + if (!/^[a-z]+$/.test(value)) { + return _('First character should be a lowercase letter (e.g., "a").'); + } else { + return true; + } + } + if (!/^[a-z][a-z0-9_-]+$/.test(value)) { + return _('Device names may contain a-z 0-9 _ or - (e.g., "my_thing1").'); + } + if (value.length > 36) { + return _('Maximum device name length is 36 characters.'); + } + return true; +} + +function validateOTP(section_id, value) { + if (value.length != 6) { + return _('Must be six characters (e.g., "S3CR3T").'); + } + return true; +} + +function firstAt(section_id, value) { + if (value && !value.startsWith('@')) { + value = '@' + value; // Ensure @ at start + } + return this.super('write', [section_id, value]); +} + +return view.extend({ + render: function() { + let m, s, o; + + m = new form.Map('sshnpd', _('NoPorts'), + _('Daemon Configuration')); + + s = m.section(form.TypedSection, 'sshnpd', _('sshnpd config')); + s.anonymous = true; + + o = s.option(form.Value, 'atsign', _('Device atSign'), + _('The device atSign e.g. @device')); + o.default = '@device'; + o.validate = validateAtsign; + o.write = firstAt; + + o = s.option(form.Value, 'manager', _('Manager atSign'), + _('The manager atSign e.g. @manager')); + o.default = '@manager'; + o.validate = validateAtsign; + o.write = firstAt; + + o = s.option(form.Value, 'device', _('Device name'), + _('The name for this device e.g. openwrt')); + o.default = 'openwrt'; + o.validate = validateDevice; + + s.option(form.Value, 'args', _('Additional arguments'), + _('Further command line arguments for the NoPorts daemon')); + + o = s.option(form.Value, 'otp', _('Enrollment OTP/SPP'), + _('One Time Passcode (OTP) for device atSign enrollment')); + o.default = '000000'; + o.validate = validateOTP; + + o = s.option(form.Flag, 'enabled', _('Enabled'), + _('Check here to enable the service')); + o.default = '1'; + o.rmempty = false; + + return m.render(); + }, +}); diff --git a/applications/luci-app-csshnpd/htdocs/luci-static/resources/view/sshnpd/enroll.js b/applications/luci-app-csshnpd/htdocs/luci-static/resources/view/sshnpd/enroll.js new file mode 100644 index 0000000000..a97cefb958 --- /dev/null +++ b/applications/luci-app-csshnpd/htdocs/luci-static/resources/view/sshnpd/enroll.js @@ -0,0 +1,97 @@ +'use strict'; +'require view'; +'require dom'; +'require fs'; +'require ui'; +'require uci'; +'require network'; + + +return view.extend({ + handleCommand: function(exec, args) { + let buttons = document.querySelectorAll('.diag-action > .cbi-button'); + + for (let i = 0; i < buttons.length; i++) + buttons[i].setAttribute('disabled', 'true'); + + return fs.exec(exec, args).then(function(res) { + let out = document.querySelector('textarea'); + + dom.content(out, [ res.stdout || '', res.stderr || '' ]); + }).catch(function(err) { + ui.addNotification(null, E('p', [ err ])) + }).finally(function() { + for (let i = 0; i < buttons.length; i++) + buttons[i].removeAttribute('disabled'); + }); + }, + + handleEnroll: function() { + return this.handleCommand('at_enroll.sh', ""); + }, + + load: function() { + return uci.load('sshnpd').then(function() { + let atsign = uci.get_first('sshnpd','','atsign'), + keyfile = '/root/.atsign/keys/'+atsign+'_key.atKeys'; + return L.resolveDefault(fs.stat(keyfile), {}); + }); + }, + + render: function(res) { + + const has_atkey = res.path; + const atsign = uci.get_first('sshnpd','','atsign'); + const device = uci.get_first('sshnpd','','device'); + const otp = uci.get_first('sshnpd','','otp'); + const enrollready = atsign && device && otp && !has_atkey; + + const instructions = E('div', { 'class': 'cbi-map-descr'}, _('Press the Enroll button then run this command on a system where '+atsign+' is activated:')); + + const enrollcmd = E('code','at_activate approve -a '+atsign+' --arx noports --drx '+device); + + let table = E('table', { 'class': 'table' }, [ + E('tr', { 'class': 'tr' }, [ + E('td', { 'class': 'td left' }, [ + E('span', { 'class': 'diag-action' }, [ + E('button', { + 'class': 'cbi-button cbi-button-action', + 'click': ui.createHandlerFn(this, 'handleEnroll') + }, [ _('Enroll') ]) + ]) + ]), + ]) + ]); + + const cmdwindow = E('div', {'class': 'cbi-section'}, [ + E('div', { 'id' : 'command-output'}, + E('textarea', { + 'id': 'widget.command-output', + 'style': 'width: 100%; font-family:monospace; white-space:pre', + 'readonly': true, + 'wrap': 'on', + 'rows': '20' + }) + ) + ]); + + let view = E('div', { 'class': 'cbi-map'}, [ + E('h2', {}, [ _('NoPorts atSign Enrollment') ]), + atsign ? E([]) : E('div', { 'class': 'cbi-map-descr'}, _('atSign must be configured')), + device ? E([]) : E('div', { 'class': 'cbi-map-descr'}, _('Device must be configured')), + otp ? E([]) : E('div', { 'class': 'cbi-map-descr'}, _('OTP must be configured. An OTP can be generated using:')), + otp ? E([]) : E('code','at_activate otp -a '+atsign), + has_atkey ? E('div', { 'class': 'cbi-map-descr'}, _('Existing key found at: '+has_atkey)) : E([]), + enrollready ? instructions : E([]), + enrollready ? enrollcmd : E([]), + enrollready ? table : E([]), + enrollready ? cmdwindow : E([]), + ]); + + return view; + }, + + handleSaveApply: null, + handleSave: null, + handleReset: null +}); diff --git a/applications/luci-app-csshnpd/root/usr/share/luci/menu.d/luci-app-csshnpd.json b/applications/luci-app-csshnpd/root/usr/share/luci/menu.d/luci-app-csshnpd.json new file mode 100644 index 0000000000..7d987bbe97 --- /dev/null +++ b/applications/luci-app-csshnpd/root/usr/share/luci/menu.d/luci-app-csshnpd.json @@ -0,0 +1,30 @@ +{ + "admin/network/sshnpd": { + "title": "NoPorts", + "order": 70, + "action": { + "type": "firstchild" + }, + "depends": { + "acl": [ "luci-app-csshnpd" ] + } + }, + + "admin/network/sshnpd/config": { + "title": "NoPorts Config", + "order": 1, + "action": { + "type": "view", + "path": "sshnpd/config" + } + }, + + "admin/network/sshnpd/enroll": { + "title": "NoPorts Enrollment", + "order": 2, + "action": { + "type": "view", + "path": "sshnpd/enroll" + } + } +} diff --git a/applications/luci-app-csshnpd/root/usr/share/rpcd/acl.d/luci-app-csshnpd.json b/applications/luci-app-csshnpd/root/usr/share/rpcd/acl.d/luci-app-csshnpd.json new file mode 100644 index 0000000000..07054b54ea --- /dev/null +++ b/applications/luci-app-csshnpd/root/usr/share/rpcd/acl.d/luci-app-csshnpd.json @@ -0,0 +1,15 @@ +{ + "luci-app-csshnpd": { + "description": "Grant UCI access for luci-app-csshnpd", + "read": { + "uci": [ "sshnpd" ], + "file": { + "/usr/bin/at_enroll.sh": ["exec"], + "/root/.atsign/keys/*": ["stat"] + } + }, + "write": { + "uci": [ "sshnpd" ] + } + } +}